home *** CD-ROM | disk | FTP | other *** search
/ Gamers Delight 2 / Gamers Delight 2.iso / Aminet / game / role / pinfocom_3_0.lha / Source / msdos.c < prev    next >
C/C++ Source or Header  |  1992-10-22  |  25KB  |  1,267 lines

  1. /* msdos.c
  2.  *
  3.  *  ``pinfocom'' -- a portable Infocom Inc. data file interpreter.
  4.  *  Copyright (C) 1987-1992  InfoTaskForce
  5.  *
  6.  *  This program is free software; you can redistribute it and/or modify
  7.  *  it under the terms of the GNU General Public License as published by
  8.  *  the Free Software Foundation; either version 2 of the License, or
  9.  *  (at your option) any later version.
  10.  *
  11.  *  This program is distributed in the hope that it will be useful,
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *  GNU General Public License for more details.
  15.  *
  16.  *  You should have received a copy of the GNU General Public License
  17.  *  along with this program; see the file COPYING.  If not, write to the
  18.  *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  */
  20.  
  21. /*
  22.  * $Header: RCS/msdos.c,v 3.0 1992/10/21 16:56:19 pds Stab $
  23.  */
  24.  
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <ctype.h>
  29. #include <errno.h>
  30.  
  31. #include <signal.h>
  32. #include <conio.h>
  33. #include <dos.h>
  34.  
  35. #include "infocom.h"
  36.  
  37. #ifndef free
  38. extern void free();
  39. #endif
  40.  
  41. #ifdef NEED_ERRNO
  42. extern int errno;
  43. #endif
  44.  
  45. /*
  46.  * Global Variables
  47.  */
  48.  
  49. /*
  50.  * Variable:    scr_usage
  51.  *
  52.  * Description:
  53.  *    This variable should contain a usage string for any extra
  54.  *    command-line options available through this terminal
  55.  *    interface.
  56.  */
  57. const char *scr_usage        = "[-C]";
  58.  
  59. /*
  60.  * Variable:    scr_long_usage
  61.  *
  62.  * Description:
  63.  *    This variable should contain a more verbose usage string
  64.  *    detailing the command-line options available through this
  65.  *    terminal interface, one option per line.
  66.  */
  67. const char *scr_long_usage = "\t-C\tdon't use color";
  68.  
  69. /*
  70.  * Variable:    scr_opt_list
  71.  *
  72.  * Description:
  73.  *    This variable should contain a getopt(3)-style option list for
  74.  *    any command-line options available through this terminal
  75.  *    interface.
  76.  */
  77. const char *scr_opt_list    = "C";
  78.  
  79. /*
  80.  * Local Variables
  81.  */
  82. #define ENV_FLAGS    "PI_FLAGS"
  83.  
  84. /*
  85.  * Options to use standard I/O, allowing redirection to/from files/devices.
  86.  * Using stream output means we can't set scroll windows.  Using stream input
  87.  * means we use DOS's command line editor for input and can't properly erase
  88.  * --More-- prompts.
  89.  */
  90. /* #define STREAM_OUT */
  91. /* #define STREAM_IN */
  92.  
  93. static FILE *scr_fp=NULL;
  94.  
  95. static int scr_columns=80, scr_indent=0, stat_size=0;
  96. static int scr_lines=24, scr_lineco=0, scr_lowleft=24;
  97. static int scr_width=80, scr_height=24;
  98.  
  99. static Bool in_status=0, allow_color=1, use_color=1;
  100.  
  101.  
  102.  
  103. #ifdef STREAM_OUT
  104. #define outstr(s)    fputs(s, stdout)
  105. #define outchar(c)    putchar(c)
  106. #else
  107. #define outstr(s)    cputs(s)
  108. #define outchar(c)    putch(c)
  109. #endif
  110. #define newline()    outchar('\r'),outchar('\n')
  111.  
  112.  
  113. #if defined(STREAM_OUT) || !defined(__TURBOC__)
  114. /*
  115.  * The following functions produce ANSI codes to produce generic video
  116.  * effects.
  117.  */
  118.  
  119. void
  120. term_clreol()
  121. {
  122.     outstr("\033[k");
  123. }
  124.  
  125. void
  126. term_clrscr()
  127. {
  128.     outstr("\033[2J");
  129. }
  130.  
  131. void
  132. term_home()
  133. {
  134.     outstr("\033[H");
  135. }
  136.  
  137. void
  138. term_lowleft()
  139. {
  140.     printf("\033[%d;0H", scr_lowleft);
  141. }
  142.  
  143. #define term_statwin()
  144.  
  145. /*
  146.  * In the future, color should be a command line option, and perhaps
  147.  * specific color settings.
  148.  *
  149.  * Some ANSI SGR parameters:
  150.  *     0  reset
  151.  *     1  bold
  152.  *     5  blink
  153.  *     7  reverse
  154.  *    3x  foreground color
  155.  *    4x  background color
  156.  *        (0-7: black/red/green/yellow/blue/magenta/cyan/white)
  157.  */
  158.  
  159. void
  160. term_text_color()
  161. {
  162.     /* white on blue, or reset (to white on black) */
  163.     outstr(use_color ? "\033[0;37;44m" : "\033[0m");
  164. }
  165.  
  166. void
  167. term_score_color()
  168. {
  169.     /* red on white, or reverse video (black on white) */
  170.     outstr(use_color ? "\033[31;47m" : "\033[7m");
  171. }
  172.  
  173. #define term_wait_color() term_score_color()
  174.  
  175. void
  176. term_status_color()
  177. {
  178.     /* white on green, or on black */
  179.     outstr(use_color ? "\033[1;42m" : "\033[1m");
  180. }
  181.  
  182. void
  183. term_mesg_color()
  184. {
  185.     /* bold red, or just bold (white) */
  186.     outstr(use_color ? "\033[1;31m" : "\033[1m");
  187. }
  188.  
  189. void
  190. term_input_color()
  191. {
  192.     /* yellow on blue */
  193.     if (use_color)
  194.     outstr("\033[1;33;44m");
  195. }
  196.  
  197. void
  198. term_termoff()
  199. {
  200.     if (use_color)
  201.     outstr("\033[0m");
  202. }
  203.  
  204. #define term_termon()
  205.  
  206. #define term_setscrl(t, b)
  207.  
  208. #else /* use convenient Turbo C library functions */
  209.  
  210. #define term_clreol()    clreol()
  211. #define term_clrscr()    clrscr()
  212.  
  213. void
  214. gotoxy A2(int, col, int, row)
  215. /*
  216.  * Note:
  217.  *    If we used the Turbo C library function, the row and column would be
  218.  *    window relative, but we want absolute.
  219.  */
  220. {
  221.     union REGS regs;
  222.  
  223.     /* Set Cursor Position */
  224.     regs.x.ax = 0x0200;
  225.     regs.x.bx = 0;
  226.     regs.h.dh = --row;
  227.     regs.h.dl = --col;
  228.     int86(0x10,®s,®s);
  229. }
  230.  
  231. #define term_home()    gotoxy(1,1)
  232. #define term_lowleft()    gotoxy(1,scr_lowleft)
  233.  
  234. void
  235. term_text_color()
  236. {
  237.     textcolor(LIGHTGRAY);
  238.     textbackground(use_color ? BLUE : BLACK);
  239. }
  240.  
  241. void
  242. term_score_color()
  243. {
  244.     textcolor(use_color ? RED : BLACK);
  245.     textbackground(LIGHTGRAY);
  246. }
  247.  
  248. #define term_wait_color() term_score_color()
  249.  
  250. void
  251. term_status_color()
  252. {
  253.     textcolor(WHITE);
  254.     textbackground(use_color ? GREEN : BLACK);
  255. }
  256.  
  257. void
  258. term_mesg_color()
  259. {
  260.     textcolor(use_color ? LIGHTRED : WHITE);
  261. }
  262.  
  263. void
  264. term_input_color()
  265. {
  266.     if (use_color)
  267.     textcolor(YELLOW);
  268. }
  269.  
  270. void
  271. term_termoff()
  272. {
  273.     textcolor(LIGHTGRAY);
  274.     textbackground(BLACK);
  275. }
  276.  
  277. #define term_termon()
  278.  
  279. void
  280. term_setscrl A2(int, top, int, bottom)
  281. {
  282.     window(1, ++top, scr_width, ++bottom);
  283. }
  284.  
  285. #endif
  286.  
  287.  
  288. /*
  289.  * Function:    get_scr_info()
  290.  *
  291.  * Arguments:
  292.  *    rows        pointer to number of rows variable
  293.  *    cols        pointer to number of columns variable
  294.  *    color_mode  pointer to boolean in-color variable
  295.  *
  296.  * Description:
  297.  *    This function does the nasty platform-specific BIOS calls to
  298.  *    determine the screen mode and dimensions.  int86() is common
  299.  *    to MS-DOS compilers including MSC and Turbo C.
  300.  */
  301. void
  302. get_scr_info A3(int *, rows, int *, cols, Bool *, color_mode)
  303. {
  304.     union REGS regs;
  305.  
  306.     /* load default row count, in case no EGA or better */
  307.     regs.h.dl = 24;
  308.  
  309.     /* Get Font Information, current font - returns rows in DL */
  310.     regs.x.bx = 0;
  311.     regs.x.ax = 0x1130;
  312.     int86(0x10,®s,®s);
  313.  
  314.     /* Get Video Mode - returns columns in AH, mode in AL */
  315.     regs.h.ah = 0x0f;
  316.     int86(0x10,®s,®s);
  317.  
  318.     /* if mode is BW 40x25, BW 80x25, or mono 80x25 */
  319.     *color_mode = !(regs.h.al == 0 || regs.h.al == 2 || regs.h.al == 7);
  320.  
  321.     /* store row/column counts */
  322.     *rows = regs.h.dl + 1;
  323.     *cols = regs.h.ah;
  324. }
  325.  
  326.  
  327. /*
  328.  * Function:    scr_cmdarg()
  329.  *
  330.  * Arguments:
  331.  *    argc        number of original arguments
  332.  *    argvp        pointer to array of strings containing args
  333.  *
  334.  * Returns:
  335.  *    New number of arguments.
  336.  *
  337.  * Description:
  338.  *    This function is called before any command line parsing is
  339.  *    done.  If the interface needs to insert any interpreter
  340.  *    options into the command-line list then they should be added
  341.  *    here; otherwise the function should do nothing.  They should be
  342.  *    added between (*argvp)[0] and (*argvp)[1].  Note that
  343.  *    (*argvp)[0] must be the command name and (*argvp)[argc] must
  344.  *    be a null pointer.
  345.  */
  346. int
  347. scr_cmdarg A2(int, argc, char ***, argvp)
  348. {
  349.     char *envp;
  350.  
  351.     /*
  352.      * If we find an ENV_FLAGS environment variable then add the flags
  353.      * into the command line.
  354.      */
  355.     if ((envp = getenv(ENV_FLAGS)) != NULL)
  356.     {
  357.     char **newv;
  358.     char *cp;
  359.     int i, newc=0;
  360.  
  361.     /*
  362.      * Count the number of args in the cmd line
  363.      */
  364.     cp = envp;
  365.     do
  366.     {
  367.         while ((*cp != '\0') && isspace(*cp))
  368.         ++cp;
  369.  
  370.         if (*cp != '\0')
  371.         ++newc;
  372.  
  373.         while ((*cp != '\0') && !isspace(*cp))
  374.         ++cp;
  375.     }
  376.     while (*cp != '\0');
  377.  
  378.     /*
  379.      * Allocate enough space for the old and new args
  380.      */
  381.     newv = (char **)xmalloc(sizeof(char *) * (argc+newc+1));
  382.     argc += newc;
  383.  
  384.     /*
  385.      * Copy the arguments into their proper places.
  386.      */
  387.     newv[0] = (*argvp)[0];
  388.     for (i=1, cp=envp; i<=newc; ++i)
  389.     {
  390.         while ((*cp != '\0') && isspace(*cp))
  391.         ++cp;
  392.  
  393.         newv[i] = cp;
  394.  
  395.         while ((*cp != '\0') && !isspace(*cp))
  396.         ++cp;
  397.         *(cp++) = '\0';
  398.     }
  399.  
  400.     for (; i<argc; ++i)
  401.         newv[i] = (*argvp)[i-newc];
  402.     newv[i] = NULL;
  403.  
  404.     *argvp = newv;
  405.     }
  406.  
  407.     return (argc);
  408. }
  409.  
  410.  
  411. /*
  412.  * Function:    scr_getopt()
  413.  *
  414.  * Arguments:
  415.  *    c        option found
  416.  *    arg        option argument (if requested)
  417.  *
  418.  * Description:
  419.  *    This function is called whenever a command-line option
  420.  *    specified in scr_opt_list (above) is found on the command
  421.  *    line.
  422.  */
  423. void
  424. scr_getopt A2(int, c, const char *, arg)
  425. {
  426.     use_color = 0;
  427. }
  428.  
  429.  
  430. /*
  431.  * Function:    scr_setup()
  432.  *
  433.  * Arguments:
  434.  *    margin        # of spaces in the right margin.
  435.  *    indent        # of spaces in the left margin.
  436.  *    scr_sz        # of lines on the screen.
  437.  *    context     # of lines of context to keep when scrolling
  438.  *
  439.  * Returns:
  440.  *    Width of screen output for informational printing (not used if
  441.  *    game is to be played).
  442.  *
  443.  * Description:
  444.  *    This function should set up generic items in the screen
  445.  *    interface that may need to be done before *any* output is
  446.  *    done.
  447.  *
  448.  *    If SCR_SZ is not 0, then this function must use SCR_SZ as the
  449.  *    number of lines the screen can hold at once, no matter what it
  450.  *    may infer otherwise.  If SCR_SZ is 0, then the function must
  451.  *    figure the size of the screen as best it can.
  452.  *
  453.  * Notes:
  454.  *    Any terminal initialization needed only for actually playing
  455.  *    the game should go in scr_begin(), not here.
  456.  */
  457. int
  458. scr_setup A4( int, margin,
  459.           int, indent,
  460.           int, scr_sz,
  461.           int, context )
  462. {
  463. #ifdef __TURBOC__
  464.     directvideo = 0;    /* use BIOS for output */
  465. #endif
  466.  
  467.     get_scr_info(&scr_height, &scr_width, &allow_color);
  468.  
  469.     if (!allow_color)
  470.     use_color = 0;
  471.  
  472.     scr_columns = scr_width;
  473.     scr_columns -= margin + indent;
  474.     scr_lines = scr_height;
  475.  
  476.     if (scr_sz)
  477.     scr_lines = scr_sz;
  478.  
  479.     scr_lowleft = scr_lines;
  480. #ifndef STREAM_OUT
  481.     scr_lines  -= context + 3;
  482.     scr_lineco -= context;
  483. #else
  484.     scr_lines  -= context + 2;
  485.     scr_lineco -= context - 1;
  486. #endif
  487.     scr_indent  = indent;
  488.  
  489.     return (scr_columns);
  490. }
  491.  
  492.  
  493. /*
  494.  * Function:    scr_shutdown()
  495.  *
  496.  * Description:
  497.  *    This function will be called just before we exit.
  498.  */
  499. void
  500. scr_shutdown()
  501. {
  502. }
  503.  
  504.  
  505. /*
  506.  * Function:    scr_begin()
  507.  *
  508.  * Arguments:
  509.  *    game    The game datafile we're about to execute.
  510.  *
  511.  * Description:
  512.  *    This function should perform terminal initializations we need
  513.  *    to actually play the game.
  514.  */
  515. void
  516. scr_begin()
  517. {
  518.     term_termon();
  519.     term_text_color();
  520.     term_clrscr();
  521.  
  522. #ifndef STREAM_OUT
  523.     /*
  524.      * Since we have scrolling, tell the interpreter
  525.      * we can handle status windows.
  526.      */
  527.     term_setscrl(gflags.pr_status ? 1 : 0, scr_lowleft-1);
  528.     F1_SETB(B_STATUS_WIN);
  529. #endif
  530.  
  531.     term_lowleft();
  532. }
  533.  
  534.  
  535. static void
  536. scr_wait A1(const char *, prompt)
  537. {
  538.     const char *outp = "\b \b";
  539.     const char *cp;
  540.  
  541.     /*
  542.      * Print the prompt,  flush the current input buffer of any
  543.      * typeahead (hopefully :-)
  544.      */
  545.     term_wait_color();
  546.  
  547.     outstr(prompt);
  548.  
  549.     term_text_color();
  550.  
  551.     setbuf(stdin, NULL);
  552.  
  553.     /*
  554.      * Get one character from keyboard, don't echo to screen.
  555.      * If character is '\0', get extended keystroke.
  556.      */
  557. #ifdef STREAM_IN
  558.     if (!fgetc(stdin)) fgetc(stdin);
  559. #else
  560.     if (!getch()) getch();
  561.  
  562.     /*
  563.      * Erase the prompt, reset the terminal, blah blah blah... we
  564.      * erase the prompt by using backspace/space/backspace chars
  565.      * instead of clear EOL or something because it's safer...
  566.      */
  567.     for (; *outp != '\0'; ++outp)
  568.     {
  569.     for (cp = prompt; *cp != '\0'; ++cp)
  570.     {
  571.         outchar(*outp);
  572.     }
  573.     }
  574. #endif
  575. }
  576.  
  577.  
  578. /*
  579.  * Function:    scr_end()
  580.  *
  581.  * Description:
  582.  *    This function will be called after the last line is printed
  583.  *    but before we exit, *only* if scr_begin() was called (*not* if
  584.  *    just scr_startup() was called!)
  585.  */
  586. void
  587. scr_end()
  588. {
  589.     /*
  590.      * Some games exit without giving you a chance to read the last
  591.      * few lines, so we ask the user to type a char before we clear
  592.      * the screen...
  593.      */
  594. #ifdef PAUSE_AT_END
  595.     scr_wait("--End--");
  596. #endif
  597.  
  598.     term_setscrl(0, scr_height-1);
  599.     term_lowleft();
  600.     term_termoff();
  601. }
  602.  
  603.  
  604. /*
  605.  * This is an internal function to print a buffer.  If flags==0 then
  606.  * print with paging and a final newline.  If flags & 1 then print
  607.  * without paging.  If flags & 2 then print up to but not including
  608.  * the last lineful, and return a pointer to it (a prompt).
  609.  */
  610. #define PB_NO_PAGING    (0x01)
  611. #define PB_PROMPT    (0x02)
  612.  
  613. static const char *
  614. scr_putbuf A4( FILE *, fp, int, flags, const char *, buf, int, max )
  615. {
  616.     const char *sp;
  617.     const char *ep;
  618.  
  619.     for (sp = buf; ;)
  620.     {
  621.     ep = chop_buf(sp, max);
  622.  
  623.     if ((*ep != '\0') || !(flags & PB_PROMPT))
  624.     {
  625.         const char *p;
  626.         int i;
  627.  
  628.         if (!(flags & PB_NO_PAGING) && (scr_lineco++ >= scr_lines))
  629.         {
  630.         scr_wait("--More--");
  631.         scr_lineco = 0;
  632.         }
  633.  
  634.         for (i=scr_indent; i>0; --i)
  635.         putc(' ', fp);
  636.  
  637.         for (p = sp; p < ep; ++p)
  638.         putc(*p, fp);
  639.  
  640.         
  641.             putc('\n', fp);
  642.     }
  643.  
  644.     if (*ep == '\0')
  645.         break;
  646.  
  647.     sp = ep + 1;
  648.     }
  649.  
  650.     return (sp);
  651. }
  652.  
  653. #ifdef STREAM_OUT
  654. #define scr_putcon(flags, buf, max)    str_putbuf(stdout, flags, buf, max)
  655. #else
  656. static const char *
  657. scr_putcon A3( int, flags, const char *, buf, int, max )
  658. {
  659.     const char *sp;
  660.     const char *ep;
  661.  
  662.     for (sp = buf; ;)
  663.     {
  664.     ep = chop_buf(sp, max);
  665.  
  666.     if ((*ep != '\0') || !(flags & PB_PROMPT))
  667.     {
  668.         const char *p;
  669.         int i;
  670.  
  671.         if (!(flags & PB_NO_PAGING) && (scr_lineco++ >= scr_lines))
  672.         {
  673.         scr_wait("--More--");
  674.         scr_lineco = 0;
  675.         }
  676.  
  677.         for (i=scr_indent; i>0; --i)
  678.         outchar(' ');
  679.  
  680.         for (p = sp; p < ep; ++p)
  681.         outchar(*p);
  682.  
  683.         newline();
  684.     }
  685.  
  686.     if (*ep == '\0')
  687.         break;
  688.  
  689.     sp = ep + 1;
  690.     }
  691.  
  692.     return (sp);
  693. }
  694. #endif
  695.  
  696. /*
  697.  * Function:    scr_putline()
  698.  *
  699.  * Arguments:
  700.  *    buffer        Line to be printed.
  701.  *
  702.  * Description:
  703.  *    This function is passed a nul-terminated string and it should
  704.  *    display the string on the terminal.  It will *not* contain a
  705.  *    newline character.
  706.  *
  707.  *    This function should perform whatever wrapping, paging, etc.
  708.  *    is necessary, print the string, and generate a final linefeed.
  709.  *
  710.  *    If the TI supports proportional-width fonts,
  711.  *    F2_IS_SET(B_FIXED_FONT) should be checked as appropriate.
  712.  *
  713.  *    If the TI supports scripting, F2_IS_SET(B_SCRIPTING) should be
  714.  *    checked as appropriate.
  715.  */
  716. void
  717. scr_putline A1(const char *, buffer)
  718. {
  719.     scr_putcon(in_status || !gflags.paged, buffer, scr_columns);
  720.  
  721.     if (F2_IS_SET(B_SCRIPTING))
  722.     scr_putbuf(scr_fp, 1, buffer, scr_width);
  723. }
  724.  
  725.  
  726. /*
  727.  * Function:    scr_putscore()
  728.  *
  729.  * Description:
  730.  *    This function prints the ti_location and ti_status strings
  731.  *    if it can and if status line printing is enabled.
  732.  */
  733. void
  734. scr_putscore()
  735. {
  736.     if (gflags.pr_status)
  737.     {
  738.     extern char *ti_location;
  739.     extern char *ti_status;
  740.  
  741.     int    i;
  742.  
  743.     /*
  744.      * Go to the "home" position (top left corner) then clear to
  745.      * the EOL, then go into reverse video mode.
  746.      */
  747.     term_home();
  748.     term_score_color();
  749.     term_clreol();
  750.  
  751.     outstr(ti_location);
  752.  
  753.     i = scr_columns+scr_indent - strlen(ti_location) - strlen(ti_status);
  754.     for (; i != 0; --i)
  755.         outchar(' ');
  756.  
  757.     outstr(ti_status);
  758.  
  759.     /*
  760.      * Turn off reverse video, then jump back down to the
  761.      * lower left corner of the screen.
  762.      */
  763.     term_text_color();
  764.     term_lowleft();
  765.     } else if (stat_size) {
  766.         /*
  767.      * Leave an empty line where the status line would be.
  768.      */
  769.     term_home();
  770.     term_score_color();
  771.     term_clreol();
  772.     term_text_color();
  773.     term_lowleft();
  774.     }
  775. }
  776.  
  777.  
  778. /*
  779.  * Function:    scr_putsound()
  780.  *
  781.  * Arguments:
  782.  *    number        sound number to play
  783.  *    action        action to perform
  784.  *    volume        volume to play sound at
  785.  *    argc        number of valid arguments
  786.  *
  787.  * Description:
  788.  *    This function plays the sound specified if it can; if not it
  789.  *    prints a line to that effect.
  790.  *
  791.  *    If the `argc' value is 1, then the we play `number' of beeps
  792.  *    (usually the ^G character).
  793.  *
  794.  *    If `argc' >1, the `action' argument is used as follows:
  795.  *
  796.  *        2:    play sound file
  797.  *        3:    stop playing sound file
  798.  *        4:    free sound resources
  799.  *
  800.  *    If `argc' >2, the `volume' argument is between 1 and 8 and is
  801.  *    a volume to play the sound at.
  802.  */
  803. void
  804. scr_putsound A4(int, number, int, action, int, volume, int, argc)
  805. {
  806.     if (argc == 1)
  807.     {
  808.     while (number--)
  809.         outchar('\a');
  810.     }
  811.     else if (argc == 3 && action == 2)
  812.     {
  813.     char buf[81];
  814.  
  815.     sprintf(buf, "Sound not supported! (number $%02x, volume %d)",
  816.         number, volume);
  817.  
  818.     scr_putmesg(buf, 0);
  819.     }
  820. }
  821.  
  822.  
  823. /*
  824.  * Function:    scr_putmesg()
  825.  *
  826.  * Arguments:
  827.  *    buffer        message string to be printed.
  828.  *    is_err        1 if message is an error message, 0 if it's not.
  829.  *
  830.  * Description:
  831.  *    This function prints out a message from the interpreter, not
  832.  *    from the game itself.  Often these are errors (IS_ERR==1)
  833.  *    but not necessarily.
  834.  */
  835. void
  836. scr_putmesg A2(const char *, buffer, Bool, is_err)
  837. {
  838.     static char *buf = "";
  839.     int blen;
  840.  
  841.     blen = strlen(buffer);
  842.     if (strlen(buf) < blen + 12)
  843.     {
  844.     if (strlen(buf) == 0)
  845.         buf = xmalloc(blen + 13);
  846.     else
  847.         buf = xrealloc(buf, blen + 13);
  848.     }
  849.  
  850.     sprintf(buf, "%s%s",
  851.         is_err ? "ERROR: " : "",
  852.         buffer);
  853.  
  854.     term_mesg_color();
  855.  
  856.     scr_putline("");
  857.     scr_putline(buf);
  858.     scr_putline("");
  859.  
  860.     term_text_color();
  861. }
  862.  
  863.  
  864. /*
  865.  * Function:    scr_getstr()
  866.  *
  867.  * Description:
  868.  *    This is an internal function which performs the meat of
  869.  *    reading a string, throwing away the excess, etc.  It returns
  870.  *    the number of characters read.
  871.  *
  872.  * Notes:
  873.  *    This function is *not* a part of the terminal interface; it's
  874.  *    just an interal helper function.
  875.  */
  876. static int
  877. scr_getstr A4( const char *, prompt,
  878.            int, length,
  879.            char *, buffer,
  880.            Bool, is_filenm )
  881. {
  882. #ifndef STREAM_IN
  883.     outstr(prompt);
  884.  
  885.     term_input_color();
  886.  
  887.     buffer[0] = length > 255 ? 253 : length - 2;
  888.     cgets(buffer);
  889.  
  890.     length = buffer[1];
  891.     strcpy(buffer, &buffer[2]);
  892.  
  893.     term_text_color();
  894.  
  895.     newline();
  896. #else
  897.     int c;
  898.  
  899.     outstr(prompt);
  900.  
  901.     term_input_color();
  902.  
  903.     buffer[0] = '\0';
  904.     buffer[length-2] = '\0';
  905.     fgets(buffer, length, stdin);
  906.  
  907.     /*
  908.      * If there's more beyond the max length of our buffer, read
  909.      * it in and throw it away...
  910.      */
  911.     if ((buffer[length-2] != '\0')
  912.     && (buffer[length-2] != '\n')
  913.     && ((c = getchar()) != EOF)
  914.     && (c != '\n'))
  915.     {
  916.     term_mesg_color();
  917.  
  918.     fputs("[Input line too long.  Flushing: `", stdout);
  919.     do
  920.     {
  921.         outchar(c);
  922.     }
  923.     while (((c = getchar()) != EOF) && (c != '\n'));
  924.  
  925.     scr_putline("']");
  926.     }
  927.     else
  928.     {
  929.     /*
  930.      * Punt the last \n...
  931.      */
  932.     length = strlen(buffer) - 1;
  933.     buffer[length] = '\0';
  934.     }
  935.  
  936.     term_text_color();
  937. #endif
  938.  
  939.     return (length);
  940. }
  941.  
  942.  
  943. /*
  944.  * Function:    scr_getline()
  945.  *
  946.  * Arguments:
  947.  *    prompt      - prompt to be printed
  948.  *    length      - total size of BUFFER
  949.  *    buffer      - buffer to return nul-terminated response in
  950.  *
  951.  * Returns:
  952.  *    # of chars stored in BUFFER
  953.  *
  954.  * Description:
  955.  *    Reads a line of input and returns it.  Handles all "special
  956.  *    operations" such as readline history support, shell escapes,
  957.  *    etc. invisibly to the caller.  Note that the returned BUFFER
  958.  *    will be at most LENGTH-1 chars long because the last char will
  959.  *    always be the nul character.
  960.  *
  961.  *    If the command begins with ESC_CHAR then it's an interpreter
  962.  *    escape command; call ti_escape() with the rest of the line,
  963.  *    then ask for another command.
  964.  *
  965.  * Notes:
  966.  *    May print the STATUS buffer more than once if necessary (i.e.,
  967.  *    a shell escape messed up the screen, a history listing was
  968.  *    generated, etc.).
  969.  */
  970. int
  971. scr_getline A3( const char *, prompt,
  972.         int,          length,
  973.         char *,       buffer )
  974. {
  975.     const char *pp;
  976.     int len;
  977.  
  978.     /*
  979.      * Loop until we've read a line.  Note that shell commands don't
  980.      * count, so if we get one then read another line.
  981.      */
  982.     for (;;)
  983.     {
  984.     /*
  985.      * Print all the prompt except the last line
  986.      */
  987.     pp = scr_putcon(PB_PROMPT, prompt, scr_columns);
  988.  
  989.     scr_putscore();
  990.     scr_lineco = 0;
  991.  
  992.     if ((len = scr_getstr(pp, length, buffer, 0)) == -1)
  993.         continue;
  994.  
  995.     if (!len)
  996.         break;
  997.  
  998.     /*
  999.      * If it's an interpreter escape, call the function then try
  1000.      * again.
  1001.      */
  1002.     if (buffer[0] == ESC_CHAR[0])
  1003.     {
  1004.         ti_escape(&buffer[1]);
  1005.         /*
  1006.          * Adjust scroll region in case the status line was toggled.
  1007.          */
  1008.         if (!stat_size) {
  1009.         term_setscrl(gflags.pr_status, scr_lowleft-1);
  1010.         term_lowleft();
  1011.         }
  1012.             scr_putscore();
  1013.         continue;
  1014.     }
  1015.  
  1016.     break;
  1017.     }
  1018.  
  1019.     if (F2_IS_SET(B_SCRIPTING))
  1020.     {
  1021.     pp = scr_putbuf(scr_fp, PB_NO_PAGING|PB_PROMPT, prompt, scr_width);
  1022.     fputs(pp, scr_fp);
  1023.     scr_putbuf(scr_fp, PB_NO_PAGING, buffer, scr_width);
  1024.     }
  1025.  
  1026.     return (len);
  1027. }
  1028.  
  1029.  
  1030. /*
  1031.  * Function:    scr_window()
  1032.  *
  1033.  * Arguments:
  1034.  *    size      - 0 to delete, non-0 means create with SIZE.
  1035.  *
  1036.  * Description:
  1037.  *    Causes a status window to be created if supported by the
  1038.  *    terminal interface; note this function won't be called unless
  1039.  *    F1_SETB(B_STATUS_WIN) is invoked in scr_begin().
  1040.  */
  1041. void
  1042. scr_window A1(int, size)
  1043. {
  1044. #ifndef STREAM_OUT
  1045.     int i;
  1046.  
  1047.     if (!size)
  1048.     {
  1049.     if (!stat_size)
  1050.         return;
  1051.  
  1052.     scr_lines += stat_size;
  1053.     stat_size = 0;
  1054.  
  1055.     term_setscrl(gflags.pr_status, scr_lowleft-1);
  1056.     scr_set_win(0);
  1057.  
  1058.     return;
  1059.     }
  1060.  
  1061.     stat_size = size;
  1062.     scr_lines -= stat_size;
  1063.     term_setscrl(stat_size+1, scr_lowleft-1);
  1064.  
  1065.     /*
  1066.      * Clean out the window
  1067.      */
  1068.     scr_set_win(1);
  1069.     for (i=stat_size; i != 0; --i)
  1070.     {
  1071.     term_clreol();
  1072.     newline();
  1073.     }
  1074.     scr_set_win(0);
  1075. #endif
  1076. }
  1077.  
  1078.  
  1079. /*
  1080.  * Function:    scr_set_win()
  1081.  *
  1082.  * Arguments:
  1083.  *    win      - 0==select text window, 1==select status window
  1084.  *
  1085.  * Description:
  1086.  *    Selects a different window.  This function won't be called
  1087.  *    unless call F1_SETB(B_STATUS_WIN) in scr_begin().
  1088.  *
  1089.  * Notes:
  1090.  */
  1091. void
  1092. scr_set_win A1(int, win)
  1093. {
  1094. #ifndef STREAM_OUT
  1095.     /*
  1096.      * Select the status window; just move the cursor there
  1097.      */
  1098.     if (win)
  1099.     {
  1100.     in_status = 1;
  1101.  
  1102.     term_home();
  1103.     newline();
  1104.     term_status_color();
  1105.     }
  1106.     else
  1107.     {
  1108.     in_status = 0;
  1109.  
  1110.     term_lowleft();
  1111.     term_text_color();
  1112.     }
  1113. #endif
  1114. }
  1115.  
  1116.  
  1117. /*
  1118.  * Function:    scr_open_sf()
  1119.  *
  1120.  * Arguments:
  1121.  *    length      - total size of BUFFER
  1122.  *    buffer      - buffer to return nul-terminated filename in
  1123.  *    type      - SF_SAVE    opening the file to save into
  1124.  *            SF_RESTORE    opening the file to restore from
  1125.  *            SF_SCRIPT    opening a file for scripting
  1126.  *
  1127.  * Returns:
  1128.  *    FILE* - reference to the opened file, or
  1129.  *    NULL  - errno==0: operation cancelled, else error opening file
  1130.  *
  1131.  * Description:
  1132.  *    Obtains the name of the file to be opened for writing (if
  1133.  *    TYPE==SF_SAVE or SF_SCRIPT) or reading (if TYPE==SF_RESTORE),
  1134.  *    opens the file with fopen(), and returns the FILE*.
  1135.  *
  1136.  *    The name of the file should be stored in BUFFER.  Upon initial
  1137.  *    calling BUFFER contains a possible default filename.
  1138.  *
  1139.  *    if TYPE==2 then don't ask the user for a name, just use
  1140.  *    BUFFER.  This means we got the -r option to restore the file.
  1141.  *    Also note that unless gflags.game_state!=NOT_INIT, scr_begin()
  1142.  *    has not been called (we're restoring before printing info).
  1143.  *
  1144.  *    If the fopen() fails just return NULL: if errno!=0 then an
  1145.  *    error will be printed.
  1146.  *
  1147.  * Notes:
  1148.  *    History is turned off here (why would anyone want it?)
  1149.  */
  1150. FILE *
  1151. scr_open_sf A3( int, length, char *, buffer, int, type )
  1152. {
  1153. #define FN_PROMPT    "Enter file name (or Q to quit)"
  1154.  
  1155.     FILE *fp;
  1156.     char prompt[MAXPATHLEN+sizeof(FN_PROMPT)+6];
  1157.     char *cp, *p;
  1158.     int len;
  1159.  
  1160.     if (length == 0)
  1161.     {
  1162.     errno = 0;
  1163.     return (fopen(buffer,
  1164.               type==SF_RESTORE ? "rb" : type==SF_SAVE ? "wb" : "w"));
  1165.     }
  1166.  
  1167.     cp = xmalloc(length);
  1168.  
  1169.  retry:
  1170.     scr_putscore();
  1171.     scr_lineco=0;
  1172.  
  1173.     sprintf(prompt, "%s [%s]: ", FN_PROMPT, buffer);
  1174.  
  1175.     len = scr_getstr(prompt, length, cp, 1);
  1176.  
  1177.     /*
  1178.      * Remove any excess whitespace from the end of the string.
  1179.      * If the input is really empty, then use the initial buffer.
  1180.      * If not copy over the string into the buffer.
  1181.      */
  1182.     for (p = &cp[len-1]; len && isspace(*p); --len, --p)
  1183.     {}
  1184.     p[1] = '\0';
  1185.  
  1186.     fp = NULL;
  1187.     errno = 0;
  1188.  
  1189.     if (((len == 1) && (cp[0] == 'q')) || (len && iscntrl(cp[0])))
  1190.     goto done;
  1191.  
  1192.     if (len)
  1193.     strcpy(buffer, cp);
  1194.  
  1195.     len = strlen(buffer);
  1196.  
  1197.     /*
  1198.      * Open the file for reading.  If we're saving and it already
  1199.      * exists then ask if the user wants to overwrite it.  If not then
  1200.      * ask for another name.
  1201.      */
  1202.     if (len)
  1203.     {
  1204.     fp = fopen(buffer, "rb");
  1205.     if (type != SF_RESTORE)
  1206.     {
  1207.         if (fp != NULL)
  1208.         {
  1209.         char over_p[MAXPATHLEN+40];
  1210.  
  1211.                 fclose(fp);
  1212.         sprintf(over_p,
  1213.             "File `%s' exists: overwrite (y/n/q) [y]? ",
  1214.             buffer);
  1215.  
  1216.         if (scr_getstr(over_p, length, cp, 1))
  1217.             if (cp[0] == 'q')
  1218.             {
  1219.             fp = NULL;
  1220.             goto done;
  1221.             }
  1222.             else if (cp[0] != 'y')
  1223.             goto retry;
  1224.         }
  1225.  
  1226.         fp = fopen(buffer, type==SF_SAVE ? "wb" : "w");
  1227.     }
  1228.     }
  1229.  
  1230.  done:
  1231.     free(cp);
  1232.  
  1233.     if (type == SF_SCRIPT)
  1234.     scr_fp = fp;
  1235.  
  1236.     return (fp);
  1237. }
  1238.  
  1239.  
  1240. /*
  1241.  * Function:    scr_close_sf()
  1242.  *
  1243.  * Arguments:
  1244.  *    filenm      - name of file just processed
  1245.  *    fp      - FILE* to open saved file
  1246.  *    type      - SF_SAVE    closing a saved game file
  1247.  *            SF_RESTORE    closing a restored game file
  1248.  *            SF_SCRIPT    closing a scripting file
  1249.  *
  1250.  * Description:
  1251.  *    This function will be called immediately after a successful
  1252.  *    save or restore of a game file, so that if the interface needs
  1253.  *    to perform any actions related to the saved game it may.  It
  1254.  *    will also be called when the interpreter notices that
  1255.  *    scripting has been turned off.    It should at least close the
  1256.  *    file.
  1257.  *
  1258.  *    This function will only be called if the save/restore of the
  1259.  *    game succeeded; if it fails the file will be closed by the
  1260.  *    interpreter.
  1261.  */
  1262. void
  1263. scr_close_sf A3( const char *, filenm, FILE *, fp, int, type )
  1264. {
  1265.     fclose(fp);
  1266. }
  1267.